package com.wdhcr.utils;

import com.alibaba.fastjson.JSONObject;
import com.thoughtworks.xstream.XStream;
import com.wdhcr.entity.User;
import com.wdhcr.entity.weixin.*;
import com.wdhcr.service.UserService;
import com.wdhcr.socket.WxWebSocketController;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.ServletInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

/*
 * @description: 操作微信公众号的工具类
 * @author: wdhcr
 * @dateTime: 2020/3/16 17:00
 */

@Component
public class WxUtil {

    @Value("${wx.token}")
    private String TOKEN;

    @Value("${wx.getTokenUrl}")
    private String GET_TOKEN_URL;

    @Value("${wx.appid}")
    private String APPID;
    @Value("${wx.appsecret}")
    private String APPSECRET;

    @Value("${wx.getUserUrl}")
    private String GET_USER_URL;

    @Value("${wx.welcomeMessage}")
    private String WELCOMEMESSAGE;


    //获取token对象
    private static AccessToken at = null;

    private HashMap<String, String> requestmap = new HashMap<>();

    @Autowired
    private JwtUtil jwtUtil;


    @Autowired
    private UserService userService;


    public HashMap<String, String> getRequestmap() {
        return requestmap;
    }

    public void setRequestmap(HashMap<String, String> requestmap) {
        this.requestmap = requestmap;
    }

    /**
     * @description: 验证签名
     * @params: [signature, timestamp, nonce, echostr]
     * @return: boolean
     * @author: wdhcr
     * @dateTime: 2019/12/30 20:35
     */
    public boolean check(String signature, String timestamp, String nonce, String echostr) {
        //1)将token，timestamp，nonce，三个参数排序

        String[] strings = {TOKEN, timestamp, nonce};
        //2)将排序后的参数拼接成一个字符串，惊醒sha1加密
        Arrays.sort(strings);
        String str = strings[0] + strings[1] + strings[2];
        //3)将加密后的字符串与signature对比，标识该请求来自微信服务器
        String mysig = sha1(str);
        return signature.equalsIgnoreCase(mysig);
    }


    /**
     * @description: 进行sha1加密
     * @params: [str]
     * @return: java.lang.String
     * @author: wdhcr
     * @dateTime: 2019/12/30 20:42
     */
    private String sha1(String str) {
        try {
            //获取加密对象
            MessageDigest sha1 = MessageDigest.getInstance("sha1");
            //加密
            byte[] digest = sha1.digest(str.getBytes());
            //处理加密结果
            char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                //高四位数
                sb.append(chars[(b >> 4) & 15]);
                //低四位数
                sb.append(chars[b & 15]);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @description: 向指定地址发送get请求
     * @params: [url]
     * @return: java.lang.String
     * @author: wdhcr
     * @dateTime: 2019/12/30 23:27
     */
    public String get(String url) {
        try {
            URL url1 = new URL(url);
            URLConnection connection = url1.openConnection();
            InputStream is = connection.getInputStream();
            byte[] bytes = new byte[1024];
            int len;
            StringBuilder sb = new StringBuilder();
            while ((len = is.read(bytes)) != -1) {
                sb.append(new String(bytes, 0, len));
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * @description: 向指定url发一个post请求
     * @params: [url, data]
     * @return: java.lang.String
     * @author: wdhcr
     * @dateTime: 2019/12/31 1:18
     */
    public String post(String url, String data) {
        try {
            URL url1 = new URL(url);
            URLConnection connection = url1.openConnection();
            //发送数据，必须设置为可发送状态
            connection.setDoOutput(true);
            OutputStream os = connection.getOutputStream();
            os.write(data.getBytes());
            os.close();
            //获取输入流对象
            InputStream is = connection.getInputStream();
            byte[] bytes = new byte[1024];
            int len;
            StringBuilder sb = new StringBuilder();
            while ((len = is.read(bytes)) != -1) {
                sb.append(new String(bytes, 0, len));
            }
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @description: 对微信服务器发送的xml数据进行解析成map返回回去。
     * @params: [is]
     * @return: java.util.Map<java.lang.String, java.lang.String>
     * @author: wdhcr
     * @dateTime: 2019/12/30 21:24
     */
    public Map<String, String> parseRequest(ServletInputStream is) {
        //获取解析对象
        SAXReader saxReader = new SAXReader();
        HashMap<String, String> map = new HashMap<>();
        try {
            //获取dom对象
            Document read = saxReader.read(is);
            //获取根节点
            Element root = read.getRootElement();
            //获取所有的子节点
            List<Element> elements = root.elements();
            for (Element element : elements) {
                //获取key和value
                map.put(element.getName(), element.getStringValue());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        requestmap = map;
        return map;
    }

    /**
     * @description: 根据用户发送的数据类型返回相应类型的xml数据包
     * @params: [requestMap]
     * @return: java.lang.String
     * @author: wdhcr
     * @dateTime: 2019/12/30 21:36
     */
    public String getRespose(Map<String, String> requestMap) {
        Message msg = null;
        String msgType = requestMap.get("MsgType");
        switch (msgType) {
            //返回文本消息
            case "text":
                msg = dealTextMessage(requestMap);

                break;
            //返回图片消息
            case "image":

                break;
            //返回链接消息
            case "link":

                break;
            //返回
            case "event":
                msg = dealEvent(requestMap);

                break;
            default:


                break;
        }
        //把消息对象处理为xml数据包
        return beanToXml(msg);
    }


    /**
     * @description: 处理事件推送的方法
     * @params: [requestMap]
     * @return: com.wdhcr.entity.Message
     * @author: wdhcr
     * @dateTime: 2019/12/31 14:19
     */
    private Message dealEvent(Map<String, String> requestMap) {
        String event = requestMap.get("Event");
        switch (event) {
            case "CLICK":
                return dealClick(requestMap);
            case "View":
                return dealView(requestMap);
            //刚关注的新用户
            case "subscribe":
                return dealSubscribe(requestMap);
            //刚取消关注的用户
            case "unsubscribe":
                return dealUnSubscribe(requestMap);
            //老用户进入
            case "SCAN":
                return dealSCAN(requestMap);
            default:
                return null;
        }
    }

    /**
     * @description: 处理用户取消事件的方法
     * @params: [requestMap]
     * @return: com.wdhcr.entity.Message
     * @author: wdhcr
     * @dateTime: 2020/1/4 22:47
     */
    private Message dealUnSubscribe(Map<String, String> requestMap) {
        return null;
    }

    /**
     * @description: 处理老用户进入时的事件推送
     * @params: [requestMap]
     * @return: com.wdhcr.entity.Message
     * @author: wdhcr
     * @dateTime: 2019/12/31 21:10
     */
    private Message dealSCAN(Map<String, String> requestMap) {
        String openId = requestMap.get("FromUserName");
        //EventKey	事件KEY值，qrscene_为前缀，后面为二维码的参数值
        String eventKey = requestMap.get("EventKey");
        //System.out.println(openId);
        String url = GET_USER_URL.replace("ACCESS_TOKEN", getAccessToken()).replace("OPENID", openId);
        /*
         * 当老用户扫描二维码时获取扫码用户的信息openid 等信息
         * */
        String result = get(url);
        JSONObject jsonObject = JSONObject.parseObject(result);
        String nickname = jsonObject.getString("nickname");
        System.out.println(result);

        /*将微信服务器的消息转发给websocket服务端推送消息，
         *实现扫描微信公众号的二维码后登录，使用的是websocket技术。
         * 将上面扫描二维码的用户信息其中包括eventkey也就是页面获取的时间戳，
         * 这些信息首先是由微信服务器推送过来，由下面的方法再推送到websocket服务端，
         * 由服务端的onmessage方法接收。
         * */
        sendUserData(eventKey, result, requestMap);
        return new TextMessage(requestMap, WELCOMEMESSAGE+"\n欢迎回来:" + nickname);

    }

    /**
     * @description: 处理新用户关注时的事件推送
     * @params: [requestMap]
     * @return: com.wdhcr.entity.Message
     * @author: wdhcr
     * @dateTime: 2019/12/31 21:10
     */
    private Message dealSubscribe(Map<String, String> requestMap) {
        String openId = requestMap.get("FromUserName");
        String eventKey = requestMap.get("EventKey");
        System.out.println("获取页面的信息id，scenid" + eventKey);
        String url = GET_USER_URL.replace("ACCESS_TOKEN", getAccessToken()).replace("OPENID", openId);
        String result = get(url);
//        System.out.println(result);
        //新用户进入二维码参数会带有前缀qrscene_，将其清除掉
        eventKey = eventKey.replace("qrscene_", "");
        sendUserData(eventKey, result, requestMap);

        return new TextMessage(requestMap, WELCOMEMESSAGE+"\n欢迎新用户:" + openId + "的到来。");
    }

    /**
     * @description: 将微信服务器发送的消息转发给websocket服务端
     * @params: [eventKey, result]eventKey二维码中所带的参数，result微信服务器推送的消息内容
     * eventKey：新用户的会带前缀qrscene_，老用户进入则不会有，传值请注意
     * 这里对微信返回的openid使用jwt生成token的形式传递
     * @return: void
     * @author: wdhcr
     * @dateTime: 2020/1/5 16:54
     */
    private void sendUserData(String eventKey, String result, Map<String, String> requestMap) {
        JSONObject jsonObject = JSONObject.parseObject(result);
        String openId = jsonObject.getString("openid");
        String nickname = jsonObject.getString("nickname");
        String city = jsonObject.getString("city");
        String province = jsonObject.getString("province");
        String country = jsonObject.getString("country");
        String headimgurl = jsonObject.getString("headimgurl");
//        System.out.println(nickname + city + province + country + headimgurl + eventKey);
        System.out.println("页面id" + eventKey);
        Hashtable<String, String> usermap = new Hashtable<>();

        /*
         * @description: 这里将用户扫描后微信推送的信息封装成对象保存到数据库中
//         */
        User user = new User(nickname, openId, country, province, city, headimgurl);
        User user1 = userService.saveUser(user);
        WxWebSocketController webSocketController = new WxWebSocketController();
        /*
         * @description: 这里为了安全起见，并保证前端权限的原因，将openid使用jwt转换成token的形式
         * 使用无状态授权给前端用户角色
         * 这时前端将一直携带此token作为凭证，请求后端数据
         */
        String token = jwtUtil.getToken(user1.getId(), openId);
        usermap.put("token", token);
        usermap.put("nickname", nickname);
        usermap.put("city", city);
        usermap.put("province", province);
        usermap.put("country", country);
        usermap.put("headimgurl", headimgurl);
        if (!StringUtils.isEmpty(eventKey)) {
            usermap.put("equipmentType", eventKey);
            String toJSONString = JSONObject.toJSONString(usermap);
            System.out.println(toJSONString);
            //将封装的消息推送给websocket服务器
            webSocketController.onMessage(toJSONString, null);
        } else {
            System.out.println("为空");

        }
    }

    /**
     * @description: 处理链接菜单的事件
     * @params: [requestMap]
     * @return: com.wdhcr.entity.Message
     * @author: wdhcr
     * @dateTime: 2019/12/31 14:24
     */
    private Message dealView(Map<String, String> requestMap) {

        return null;

    }

    /**
     * @description: 处理点击菜单的事件
     * @params: [requestMap]
     * @return: com.wdhcr.entity.Message
     * @author: wdhcr
     * @dateTime: 2019/12/31 14:23
     */
    private Message dealClick(Map<String, String> requestMap) {

        String eventKey = requestMap.get("EventKey");
        switch (eventKey) {
            case "1":
                String openId = requestMap.get("FromUserName");
                return new TextMessage(requestMap, "当前用户的OpenId:" + openId);
            case "31":
                return new TextMessage(requestMap, "你点击了第三个一级菜单的子菜单！！1");

            default:
                break;
        }

        return null;
    }

    /**
     * @description: 将消息对象转换成xml数据包
     * @params: [msg]
     * @return: java.lang.String
     * @author: wdhcr
     * @dateTime: 2019/12/30 22:33
     */
    public String beanToXml(Message msg) {
        //创建xml对象
        XStream xStream = new XStream();
        //开启需要使用注解的类
        xStream.processAnnotations(TextMessage.class);
        xStream.processAnnotations(ImageMessage.class);
        xStream.processAnnotations(NewsMessage.class);
        return xStream.toXML(msg);
    }


    /**
     * @description: 获取token
     * @params: []
     * @return: void
     * @author: wdhcr
     * @dateTime: 2019/12/30 23:36
     */
    private void getToken() {
        String url = GET_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
        String tokenStr = get(url);
        JSONObject jsonObject = JSONObject.parseObject(tokenStr);
        String access_token = jsonObject.getString("access_token");
        String expires_in = jsonObject.getString("expires_in");
        at = new AccessToken(access_token, expires_in);

    }

    /**
     * @description: 向外界暴漏获取token的方法，保证外界每次能获取到有效的token
     * @params: []
     * @return: java.lang.String
     * @author: wdhcr
     * @dateTime: 2019/12/30 23:57
     */
    public String getAccessToken() {
        if (at == null || at.isExpired()) {
            //获取token
            getToken();
        }
        //向外返回token
        return at.getAccessToken();
    }

    /**
     * @description: 生成临时的带参数的二维码的ticket
     * @params: []
     * @return: java.lang.String
     * @author: wdhcr
     * @dateTime: 2019/12/31 17:09
     */
    public String getQrCodeTicket(int sceneId) {
        String at = getAccessToken();
//        System.out.println(at);
        String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + at;
        String data = "{\"expire_seconds\": 604800, \"action_name\": \"QR_SCENE\", \"action_info\":" +
                " {\"scene\": {\"scene_id\": " + sceneId +
                "}}}";
//        System.out.println("data  = "+data);
        String result = post(url, data);
//        System.out.println(result);
        return JSONObject.parseObject(result).getString("ticket");
    }


    /**
     * @description: 处理文本消息
     * @params: [requestMap]
     * @return: com.wdhcr.entity.Message
     * @author: wdhcr
     * @dateTime: 2019/12/30 21:48
     */
    private Message dealTextMessage(Map<String, String> requestMap) {
        //当用户发送图文时，推送图文消息
        String content = requestMap.get("Content");
//        System.out.println(content);
        if ("图文".equals(content)) {

            //返回图文消息对象
            return dealNewsMessage(requestMap);
        }
        TextMessage tm = new TextMessage(requestMap, "你好啊！！！");
        return tm;
    }


    /*
     * @description: 发送图文消息当博主每发表一篇博客时，将向关注的用户推送。
     * @params: [requestMap]
     * @return: com.wdhcr.entity.weixin.Message
     * @author: wdhcr
     * @dateTime: 2020/3/16 18:26
     */
    private Message dealNewsMessage(Map<String, String> requestMap) {
        ArrayList<Article> articles = new ArrayList<>();
        articles.add(new Article("使用frp搭建内网穿透", "本篇博客讲述如何使用frp搭建内网穿透服务...", "https://picsum.photos/id/237/300/200", "http://wdhcr.xyz:8080/blog/4"));

        NewsMessage newsMessage = new NewsMessage(requestMap, articles);
        return newsMessage;
    }

    /*
     * @description: 当发布新的博客内容时，向所有关注的用户推送
     * @params: [openid, article]
     * @return: void
     * @author: wdhcr
     * @dateTime: 2020/3/23 20:04
     */
    public void sendNewsMessage(String openid, Article article) {
        ArrayList<Object> articles = new ArrayList<>();
        articles.add(article);
        String s = JSONObject.toJSONString(articles);
        String json = "{\"touser\":\"" + openid + "\",\"msgtype\":\"news\",\"news\":" +
                "{\"articles\":" + s + "}" + "}";
        json = json.replace("picUrl", "picurl");

        post("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + getAccessToken(), json);
    }

    public void sendTemplateMessage(String templeteId,String templeteMessage,String from,String to,String message,String blogUrl){
        String at = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+at;
        //推送模板消息的数据包，格式为json格式
        String data = "{\n" +
                "    \"touser\": \""+to+"\",\n" +
                "    \"template_id\": \""+templeteId+"\",\n" +
                "    \"url\": \""+blogUrl+"\",\n" +
                "    \"data\": {\n" +
                "        \"first\": {\n" +
                "            \"value\": \""+templeteMessage+"\",\n" +
                "            \"color\": \"#CD0023\"\n" +
                "        },\n" +
                "        \"from\": {\n" +
                "            \"value\": \""+from+"\",\n" +
                "            \"color\": \"#4032FF\"\n" +
                "        },\n" +
                "        \"time\": {\n" +
                "            \"value\": \""+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) +"\",\n" +
                "            \"color\": \"#48CFFF\"\n" +
                "        },\n" +
                "        \"result\": {\n" +
                "            \"value\": \""+message+"\",\n" +
                "            \"color\": \"#DB1967\"\n" +
                "        }\n" +
                "    }\n" +
                "}";
        String result = post(url, data);
        System.out.println(result);
    }


}
